home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / DirectInput / DIConfig / cdeviceview.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-10-08  |  19.5 KB  |  857 lines

  1. //-----------------------------------------------------------------------------
  2. // File: cdeviceview.cpp
  3. //
  4. // Desc: CDeviceView is a window class derived from CFlexWnd.  It represents
  5. //       the device view window in which the device and callouts are drawn.
  6. //       Each CDeviceView only represents one view.  A device that has more
  7. //       than one view should have a corresponding number of CDeviceView for it.
  8. //
  9. // Copyright (C) 1999-2001 Microsoft Corporation. All Rights Reserved.
  10. //-----------------------------------------------------------------------------
  11.  
  12. #include "common.hpp"
  13.  
  14.  
  15. CDeviceView::CDeviceView(CDeviceUI &ui) :
  16.     m_ui(ui),
  17.     m_pbmImage(NULL),
  18.     m_pbmThumb(NULL),
  19.     m_pbmSelThumb(NULL),
  20.     m_SuperState(0),
  21.     m_State(0),
  22.     m_SubState(0),
  23.     m_OldSuperState(0),
  24.     m_OldState(0),
  25.     m_OldSubState(0),
  26.     m_pControlContext(NULL),
  27.     m_ptszImagePath(NULL),
  28.     m_bScrollEnable(FALSE),
  29.     m_nScrollOffset(0),
  30.     m_nViewHeight(g_sizeImage.cy),
  31.     m_bForcePaint(FALSE)
  32. {
  33.     m_ptNextWLOText.x = m_ptNextWLOText.y = 0;
  34. }
  35.  
  36. CDeviceView::~CDeviceView()
  37. {
  38.     Unpopulate();
  39. }
  40.  
  41. CDeviceControl *CDeviceView::NewControl()
  42. {
  43.     CDeviceControl *pControl = new CDeviceControl(m_ui, *this);
  44.     if (!pControl)
  45.         return NULL;
  46.     m_arpControl.SetAtGrow(m_arpControl.GetSize(), pControl);
  47.     return pControl;
  48. }
  49.  
  50. void CDeviceView::Remove(CDeviceControl *pControl)
  51. {
  52.     if (pControl == NULL)
  53.         return;
  54.  
  55.     int i = pControl->GetControlIndex();
  56.     if (i < 0 || i >= GetNumControls())
  57.     {
  58.         assert(0);
  59.         return;
  60.     }
  61.  
  62.     if (pControl == m_pControlContext)
  63.         m_pControlContext = NULL;
  64.  
  65.     if (m_arpControl[i] != NULL)
  66.         delete m_arpControl[i];
  67.     m_arpControl[i] = NULL;
  68.  
  69.     m_arpControl.RemoveAt(i);
  70.  
  71.     Invalidate();
  72. }
  73.  
  74. void CDeviceView::RemoveAll(BOOL bUser)
  75. {
  76.     m_pControlContext = NULL;
  77.  
  78.     for (int i = 0; i < GetNumControls(); i++)
  79.     {
  80.         if (m_arpControl[i] != NULL)
  81.             delete m_arpControl[i];
  82.         m_arpControl[i] = NULL;
  83.     }
  84.     m_arpControl.RemoveAll();
  85.  
  86.     Invalidate();
  87. }
  88.  
  89. void CDeviceView::Unpopulate(BOOL bInternalOnly)
  90. {
  91.     DisableScrollBar();
  92.  
  93.     m_bScrollEnable = FALSE;
  94.  
  95.     if (m_pbmImage != NULL)
  96.         delete m_pbmImage;
  97.     if (m_pbmThumb != NULL)
  98.         delete m_pbmThumb;
  99.     if (m_pbmSelThumb != NULL)
  100.         delete m_pbmSelThumb;
  101.     m_pbmImage = NULL;
  102.     m_pbmThumb = NULL;
  103.     m_pbmSelThumb = NULL;
  104.     free(m_ptszImagePath);
  105.     m_ptszImagePath = NULL;
  106.  
  107.     if (!bInternalOnly)
  108.         RemoveAll(FALSE);
  109.  
  110.     for (int i = 0; i < m_arpText.GetSize(); i++)
  111.     {
  112.         if (m_arpText[i])
  113.             delete m_arpText[i];
  114.         m_arpText[i] = NULL;
  115.     }
  116.     m_arpText.RemoveAll();
  117. }
  118.  
  119. void AssureSize(CBitmap *&pbm, SIZE to)
  120. {
  121.     if (!pbm)
  122.         return;
  123.  
  124.     SIZE from;
  125.     if (!pbm->GetSize(&from))
  126.         return;
  127.  
  128.     if (from.cx >= to.cx && from.cy >= to.cy)
  129.         return;
  130.  
  131.     CBitmap *nbm = CBitmap::Create(to, RGB(0,0,0));
  132.     if (!nbm)
  133.         return;
  134.  
  135.     HDC hDC = nbm->BeginPaintInto();
  136.     pbm->Draw(hDC);
  137.     nbm->EndPaintInto(hDC);
  138.  
  139.     delete pbm;
  140.     pbm = nbm;
  141.     nbm = NULL;
  142. }
  143.  
  144. CBitmap *CDeviceView::GrabViewImage()
  145. {
  146.     CBitmap *pbm = CBitmap::Create(GetClientSize(), RGB(0, 0, 0), NULL);
  147.     if (!pbm)
  148.         return NULL;
  149.     HDC hDC = pbm->BeginPaintInto();
  150.     if (!hDC)
  151.     {
  152.         delete pbm;
  153.         return NULL;
  154.     }
  155.  
  156.     OnPaint(hDC);
  157.  
  158.     pbm->EndPaintInto(hDC);
  159.  
  160.     return pbm;
  161. }
  162.  
  163. void CDeviceView::MakeMissingImages()
  164. {
  165. //    if (m_pbmImage)
  166. //        AssureSize(m_pbmImage, g_sizeImage);
  167.  
  168.     if (m_pbmThumb == NULL)
  169.     {
  170.         if (m_pbmImage)
  171.             m_pbmThumb = m_pbmImage->CreateResizedTo(g_sizeThumb);
  172.         else
  173.         {
  174.             CBitmap *pbmImage = GrabViewImage();
  175.             if (pbmImage)
  176.             {
  177.                 AssureSize(pbmImage, g_sizeImage);
  178.                 m_pbmThumb = pbmImage->CreateResizedTo(g_sizeThumb);
  179.             }
  180.             delete pbmImage;
  181.         }
  182.     }
  183.  
  184.     if (m_pbmThumb == NULL)
  185.         return;
  186.  
  187.     if (m_pbmSelThumb == NULL)
  188.     {
  189.         m_pbmSelThumb = m_pbmThumb->Dup();
  190.         if (m_pbmSelThumb != NULL)
  191.         {
  192.             HDC hDC = m_pbmSelThumb->BeginPaintInto();
  193.             {
  194.                 CPaintHelper ph(m_ui.m_uig, hDC);
  195.                 ph.SetPen(UIP_SELTHUMB);
  196.                 ph.Rectangle(0, 0, g_sizeThumb.cx, g_sizeThumb.cy, UIR_OUTLINE);
  197.             }
  198.             m_pbmSelThumb->EndPaintInto(hDC);
  199.         }
  200.     }
  201. }
  202.  
  203. void CDeviceView::OnPaint(HDC hDC)
  204. {
  205.     HDC hBDC = NULL, hODC = NULL;
  206.     CBitmap *pbm = NULL;
  207.  
  208.     if (!InRenderMode())
  209.     {
  210.         hODC = hDC;
  211.         pbm = CBitmap::Create(GetClientSize(), RGB(0, 0, 0), hDC);
  212.         if (pbm != NULL)
  213.         {
  214.             hBDC = pbm->BeginPaintInto();
  215.             if (hBDC != NULL)
  216.                 hDC = hBDC;
  217.         }
  218.     }
  219.  
  220.     // Black-fill first
  221.     SIZE fillsz = GetClientSize();
  222.     RECT fillrc = {0, 0, fillsz.cx, fillsz.cy};
  223.     FillRect(hDC, &fillrc, (HBRUSH)GetStockObject(BLACK_BRUSH));
  224.  
  225.     if (m_pbmImage != NULL)
  226.         m_pbmImage->Blend(hDC);
  227.  
  228.     BOOL bScroll = m_bScrollEnable && m_sb.m_hWnd;
  229.     int sdc = 0;
  230.     if (bScroll)
  231.     {
  232.         sdc = SaveDC(hDC);
  233.         OffsetViewportOrgEx(hDC, 0, -m_nScrollOffset + g_iListHeaderHeight, NULL);
  234.     }
  235.     else
  236.     if (m_bScrollEnable)
  237.     {
  238.         sdc = SaveDC(hDC);
  239.         OffsetViewportOrgEx(hDC, 0, g_iListHeaderHeight, NULL);
  240.     }
  241.  
  242.     int miny = 0 + m_nScrollOffset;
  243.     int maxy = g_sizeImage.cy + m_nScrollOffset;
  244.  
  245.     int t, nt = GetNumTexts();
  246.     for (t = 0; t < nt; t++)
  247.     {
  248.         CDeviceViewText *pText = m_arpText[t];
  249.         if (pText != NULL &&
  250.             !(pText->GetMinY() > maxy || pText->GetMaxY() < miny))
  251.                 pText->OnPaint(hDC);
  252.     }
  253.  
  254.     BOOL bCFGUIEdit = m_ui.m_uig.InEditMode();
  255.     BOOL bEitherEditMode = bCFGUIEdit;
  256.  
  257.     int c, nc = GetNumControls();
  258.     for (c = 0; c < nc; c++)
  259.         if (m_arpControl[c] != NULL && m_arpControl[c]->HasOverlay() &&
  260.             (m_arpControl[c]->IsHighlighted()
  261.                 )
  262.                 && (bEitherEditMode || m_arpControl[c]->IsMapped()))
  263.             m_arpControl[c]->DrawOverlay(hDC);
  264.     for (c = 0; c < nc; c++)
  265.     {
  266.         CDeviceControl *pControl = m_arpControl[c];
  267.         if (pControl != NULL && (bEitherEditMode || pControl->IsMapped()) &&
  268.             !(pControl->GetMinY() > maxy || pControl->GetMaxY() < miny))
  269.             pControl->OnPaint(hDC);
  270.     }
  271.  
  272.     if (bScroll || m_bScrollEnable)
  273.     {
  274.         RestoreDC(hDC, sdc);
  275.         sdc = 0;
  276.     }
  277.  
  278.     // Black fill the top portion if this is a list view
  279.     if (bScroll)
  280.     {
  281.         GetClientRect(&fillrc);
  282.         fillrc.bottom = g_iListHeaderHeight;
  283.         FillRect(hDC, &fillrc, (HBRUSH)GetStockObject(BLACK_BRUSH));
  284.     }
  285.  
  286.     // Print out the headers
  287.     TCHAR tszHeader[MAX_PATH];
  288.     // Control column
  289.     if (m_arpText.GetSize())
  290.     {
  291.         CPaintHelper ph(m_ui.m_uig, hDC);
  292.         ph.SetElement(UIE_CALLOUT);
  293.  
  294.         for (int i = 0; i < 2; i++)
  295.         {
  296.             // Check if there are two columns, break out the 2nd iteration if not 2 columns.
  297.             if (i == 1 && !(GetNumControls() > 1 &&
  298.                 m_arpControl[0]->GetCalloutMaxRect().top == m_arpControl[1]->GetCalloutMaxRect().top))
  299.                 break;
  300.  
  301.             RECT rcheader;
  302.             if (m_arpText.GetSize())
  303.             {
  304.                 rcheader = m_arpText[i]->GetRect();
  305.                 rcheader.bottom -= rcheader.top;
  306.                 rcheader.top = 0;
  307.                 LoadString(g_hModule, IDS_LISTHEADER_CTRL, tszHeader, MAX_PATH);
  308.                 DrawText(hDC, tszHeader, -1, &rcheader, DT_LEFT|DT_NOPREFIX|DT_CALCRECT);
  309.                 if (rcheader.right > m_arpText[i]->GetRect().right)
  310.                     rcheader.left -= rcheader.right - m_arpText[i]->GetRect().right;
  311.                 DrawText(hDC, tszHeader, -1, &rcheader, DT_LEFT|DT_NOPREFIX);
  312.  
  313.                 // Action column
  314.                 rcheader = m_arpControl[i]->GetCalloutMaxRect();
  315.                 rcheader.bottom -= rcheader.top;
  316.                 rcheader.top = 0;
  317.                 LoadString(g_hModule, IDS_LISTHEADER_ACTION, tszHeader, MAX_PATH);
  318.                 DrawText(hDC, tszHeader, -1, &rcheader, DT_CENTER|DT_NOPREFIX);
  319.             }
  320.         }
  321.     }
  322.  
  323.  
  324.     if (!InRenderMode())
  325.     {
  326.         if (pbm != NULL)
  327.         {
  328.             if (hBDC != NULL)
  329.             {
  330.                 pbm->EndPaintInto(hBDC);
  331.                 pbm->Draw(hODC);
  332.             }
  333.             delete pbm;
  334.         }
  335.     }
  336. }
  337.  
  338. int CDeviceView::GetNumControls()
  339. {
  340.     return m_arpControl.GetSize();
  341. }
  342.  
  343. CDeviceControl *CDeviceView::GetControl(int nControl)
  344. {
  345.     if (nControl >= 0 && nControl < GetNumControls())
  346.         return m_arpControl[nControl];
  347.     else
  348.         return NULL;
  349. }
  350.  
  351. CBitmap *CDeviceView::GetImage(DVIMAGE dvi)
  352. {
  353.     switch (dvi)
  354.     {
  355.         case DVI_IMAGE: return m_pbmImage;
  356.         case DVI_THUMB: return m_pbmThumb;
  357.         case DVI_SELTHUMB: return m_pbmSelThumb;
  358.  
  359.         default:
  360.             return NULL;
  361.     }
  362. }
  363.  
  364. void CDeviceView::OnMouseOver(POINT point, WPARAM wParam)
  365. {
  366.     if (m_bScrollEnable && m_sb.m_hWnd)
  367.         point.y += m_nScrollOffset;
  368.  
  369.  
  370.     // Check if we are over a control
  371.     POINT adjPt = point;
  372.     if (m_bScrollEnable) adjPt.y -= g_iListHeaderHeight;
  373.     int c, nc = GetNumControls();
  374.     for (c = 0; c < nc; c++)
  375.         if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(adjPt) != DCHT_NOHIT)
  376.         {
  377.             m_arpControl[c]->OnMouseOver(adjPt);
  378.             return;
  379.         }
  380.  
  381.     // Check if we are over a viewtext
  382.     nc = GetNumTexts();
  383.     for (c = 0; c < nc; c++)
  384.         if (m_arpText[c] != NULL && m_arpText[c]->HitTest(adjPt) != DCHT_NOHIT)
  385.         {
  386.             m_arpText[c]->OnMouseOver(adjPt);
  387.             return;
  388.         }
  389.  
  390.     CFlexWnd::s_ToolTip.SetEnable(FALSE);
  391.  
  392.     DEVICEUINOTIFY uin;
  393.     uin.msg = DEVUINM_MOUSEOVER;
  394.     uin.from = DEVUINFROM_VIEWWND;
  395.     uin.mouseover.point = point;
  396.     m_ui.Notify(uin);
  397. }
  398.  
  399. void CDeviceView::OnClick(POINT point, WPARAM wParam, BOOL bLeft)
  400. {
  401.     if (m_bScrollEnable && m_sb.m_hWnd)
  402.         point.y += m_nScrollOffset;
  403.  
  404.  
  405.     POINT adjPt = point;
  406.     if (m_bScrollEnable) adjPt.y -= g_iListHeaderHeight;
  407.     int c, nc = GetNumControls();
  408.     for (c = 0; c < nc; c++)
  409.         // adjPt is the adjust click point for scrolling list view
  410.         if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(adjPt) != DCHT_NOHIT)
  411.         {
  412.             m_arpControl[c]->OnClick(adjPt, bLeft);
  413.             return;
  414.         }
  415.  
  416.     {
  417.         for (c = 0; c < GetNumTexts(); ++c)
  418.             if (m_arpControl[c] != NULL && m_arpText[c] != NULL)
  419.             {
  420.                 RECT rc = m_arpText[c]->GetRect();
  421.                 if (PtInRect(&rc, adjPt))
  422.                 {
  423.                     m_arpControl[c]->OnClick(adjPt, bLeft);
  424.                     return;
  425.                 }
  426.             }
  427.     }
  428.  
  429.  
  430.     // Send notification
  431.     DEVICEUINOTIFY uin;
  432.     uin.msg = DEVUINM_CLICK;
  433.     uin.from = DEVUINFROM_VIEWWND;
  434.     uin.click.bLeftButton = bLeft;
  435.     m_ui.Notify(uin);
  436. }
  437.  
  438. void CDeviceView::OnDoubleClick(POINT point, WPARAM wParam, BOOL bLeft)
  439. {
  440.     if (m_bScrollEnable && m_sb.m_hWnd)
  441.         point.y += m_nScrollOffset;
  442.  
  443.     POINT adjPt = point;
  444.     if (m_bScrollEnable) adjPt.y -= g_iListHeaderHeight;
  445.     int c, nc = GetNumControls();
  446.     for (c = 0; c < nc; c++)
  447.         if (m_arpControl[c] != NULL && m_arpControl[c]->HitTest(adjPt) != DCHT_NOHIT)
  448.         {
  449.             m_arpControl[c]->OnClick(adjPt, bLeft, TRUE);
  450.             return;
  451.         }
  452.  
  453.     for (c = 0; c < GetNumTexts(); ++c)
  454.         if (m_arpControl[c] != NULL && m_arpText[c] != NULL)
  455.         {
  456.             RECT rc = m_arpText[c]->GetRect();
  457.             if (PtInRect(&rc, adjPt))
  458.             {
  459.                 m_arpControl[c]->OnClick(adjPt, bLeft, TRUE);
  460.                 return;
  461.             }
  462.         }
  463.  
  464.     DEVICEUINOTIFY uin;
  465.     uin.msg = DEVUINM_DOUBLECLICK;
  466.     uin.from = DEVUINFROM_VIEWWND;
  467.     uin.click.bLeftButton = bLeft;
  468.     m_ui.Notify(uin);
  469. }
  470.  
  471. void CDeviceView::OnWheel(POINT point, WPARAM wParam)
  472. {
  473.     if (!m_bScrollEnable) return;
  474.  
  475.     if (m_sb.GetMin() == m_sb.GetMax()) return;
  476.  
  477.     int nPage = MulDiv(m_sb.GetPage(), 9, 10) >> 1;  // Half a page at a time
  478.  
  479.     if ((int)wParam >= 0)
  480.         m_sb.AdjustPos(-nPage);
  481.     else
  482.         m_sb.AdjustPos(nPage);
  483.  
  484.     m_nScrollOffset = m_sb.GetPos();
  485.     Invalidate();
  486. }
  487.  
  488.  
  489. BOOL CDeviceView::DoesCalloutExistForOffset(DWORD dwOfs)
  490. {
  491.     return DoesCalloutOtherThanSpecifiedExistForOffset(NULL, dwOfs);
  492. }
  493.  
  494. BOOL CDeviceView::DoesCalloutOtherThanSpecifiedExistForOffset(CDeviceControl *pOther, DWORD dwOfs)
  495. {
  496.     int nc = GetNumControls();
  497.     for (int i = 0; i < nc; i++)
  498.     {
  499.         CDeviceControl *pControl = GetControl(i);
  500.         if (pControl == NULL || pControl == pOther)
  501.             continue;
  502.         if (!pControl->IsOffsetAssigned())
  503.             continue;
  504.         if (pControl->GetOffset() == dwOfs)
  505.             return TRUE;
  506.     }
  507.     return FALSE;
  508. }
  509.  
  510. // This function returns the index of a control with the specified offset
  511. int CDeviceView::GetIndexFromOfs(DWORD dwOfs)
  512. {
  513.     for (int i = 0; i < GetNumControls(); ++i)
  514.         if (m_arpControl[i]->GetOffset() == dwOfs)
  515.             return i;
  516.  
  517.     return -1;
  518. }
  519.  
  520.  
  521. int CDeviceView::GetViewIndex()
  522. {
  523.     return m_ui.GetViewIndex(this);
  524. }
  525.  
  526.  
  527. BOOL CDeviceView::IsUnassignedOffsetAvailable()
  528. {
  529.     DIDEVOBJSTRUCT os;
  530.  
  531.     HRESULT hr = FillDIDeviceObjectStruct(os, m_ui.m_lpDID);
  532.     if (FAILED(hr))
  533.         return FALSE;
  534.  
  535.     if (os.nObjects < 1)
  536.         return FALSE;
  537.  
  538.     assert(os.pdoi);
  539.     if (!os.pdoi)
  540.         return FALSE;
  541.  
  542.     for (int i = 0; i < os.nObjects; i++)
  543.     {
  544.         const DIDEVICEOBJECTINSTANCEW &o = os.pdoi[i];
  545.  
  546.         if (!DoesCalloutExistForOffset(o.dwOfs))
  547.             return TRUE;
  548.     }
  549.  
  550.     return FALSE;
  551. }
  552.  
  553. CDeviceViewText *CDeviceView::AddText(
  554.     HFONT f, COLORREF t, COLORREF b, const RECT &r, LPCTSTR text)
  555. {
  556.     CDeviceViewText *pText = NewText();
  557.     if (!pText)
  558.         return NULL;
  559.  
  560.     pText->SetLook(f, t, b);
  561.     pText->SetRect(r);
  562.     pText->SetText(text);
  563.  
  564.     return pText;
  565. }
  566.  
  567. CDeviceViewText *CDeviceView::AddText(
  568.     HFONT f, COLORREF t, COLORREF b, const POINT &p, LPCTSTR text)
  569. {
  570.     CDeviceViewText *pText = NewText();
  571.     if (!pText)
  572.         return NULL;
  573.  
  574.     pText->SetLook(f, t, b);
  575.     pText->SetPosition(p);
  576.     pText->SetTextAndResizeTo(text);
  577.  
  578.     return pText;
  579. }
  580.  
  581. CDeviceViewText *CDeviceView::AddWrappedLineOfText(
  582.     HFONT f, COLORREF t, COLORREF b, LPCTSTR text)
  583. {
  584.     CDeviceViewText *pText = NewText();
  585.     if (!pText)
  586.         return NULL;
  587.  
  588.     pText->SetLook(f, t, b);
  589.     pText->SetPosition(m_ptNextWLOText);
  590.     pText->SetTextAndResizeToWrapped(text);
  591.     
  592.     m_ptNextWLOText.y += pText->GetHeight();
  593.  
  594.     return pText;
  595. }
  596.  
  597. CDeviceViewText *CDeviceView::NewText()
  598. {
  599.     CDeviceViewText *pText = new CDeviceViewText(m_ui, *this);
  600.     if (!pText)
  601.         return NULL;
  602.     m_arpText.SetAtGrow(m_arpText.GetSize(), pText);
  603.     return pText;
  604. }
  605.  
  606. int CDeviceView::GetNumTexts()
  607. {
  608.     return m_arpText.GetSize();
  609. }
  610.  
  611. CDeviceViewText *CDeviceView::GetText(int nText)
  612. {
  613.     if (nText < 0 || nText >= GetNumTexts())
  614.         return NULL;
  615.     return m_arpText[nText];
  616. }
  617.  
  618. void CDeviceView::SetImage(CBitmap *&refpbm)
  619. {
  620.     delete m_pbmImage;
  621.     m_pbmImage = refpbm;
  622.     refpbm = NULL;
  623.     MakeMissingImages();
  624.     Invalidate();
  625. }
  626.  
  627. void CDeviceView::SetImagePath(LPCTSTR tszPath)
  628. {
  629.     if (m_ptszImagePath)
  630.         free(m_ptszImagePath);
  631.     m_ptszImagePath = NULL;
  632.  
  633.     if (tszPath)
  634.         m_ptszImagePath = _tcsdup(tszPath);
  635. }
  636.  
  637. void CDeviceView::CalcDimensions()
  638. {
  639.     // go through all texts and controls to find the max y coord
  640.     int max = g_sizeImage.cy - g_iListHeaderHeight;
  641.     int i = 0;
  642.     for (; i < GetNumTexts(); i++)
  643.     {
  644.         CDeviceViewText *pText = GetText(i);
  645.         if (!pText)
  646.             continue;
  647.         int ty = pText->GetMaxY();
  648.         if (ty > max)
  649.             max = ty;
  650.     }
  651.     for (i = 0; i < GetNumControls(); i++)
  652.     {
  653.         CDeviceControl *pControl = GetControl(i);
  654.         if (!pControl)
  655.             continue;
  656.         int cy = pControl->GetMaxY();
  657.         if (cy > max)
  658.             max = cy;
  659.     }
  660.  
  661.     // set
  662.     m_nViewHeight = max;
  663.     m_nScrollOffset = 0;
  664.  
  665.     // enable scrollbar if view height more than window size
  666.     if (m_nViewHeight > g_sizeImage.cy - g_iListHeaderHeight)
  667.         EnableScrollBar();
  668. }
  669.  
  670. void CDeviceView::DisableScrollBar()
  671. {
  672.     if (!m_sb.m_hWnd)
  673.         return;
  674.  
  675.     m_sb.Destroy();
  676. }
  677.  
  678. void CDeviceView::EnableScrollBar()
  679. {
  680.     if (m_sb.m_hWnd)
  681.         return;
  682.  
  683.     FLEXSCROLLBARCREATESTRUCT cs;
  684.     cs.dwSize = sizeof(cs);
  685.     cs.dwFlags = FSBF_VERT;
  686.     cs.min = 0;
  687.     cs.max = m_nViewHeight;
  688.     cs.page = g_sizeImage.cy - g_iListHeaderHeight;
  689.     cs.pos = m_nScrollOffset;
  690.     cs.hWndParent = m_hWnd;
  691.     cs.hWndNotify = m_hWnd;
  692.     RECT rect = {g_sizeImage.cx - DEFAULTVIEWSBWIDTH, g_iListHeaderHeight, g_sizeImage.cx, g_sizeImage.cy};
  693.     cs.rect = rect;
  694.     cs.bVisible = TRUE;
  695.     m_sb.SetColors(
  696.         m_ui.m_uig.GetBrushColor(UIE_SBTRACK),
  697.         m_ui.m_uig.GetBrushColor(UIE_SBTHUMB),
  698.         m_ui.m_uig.GetPenColor(UIE_SBBUTTON));
  699.     m_sb.Create(&cs);
  700. }
  701.  
  702. LRESULT CDeviceView::WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
  703. {
  704.     switch (msg)
  705.     {
  706.         case WM_PAINT:
  707.             m_bForcePaint = TRUE;
  708.             return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  709.  
  710.         case WM_FLEXVSCROLL:
  711.         {
  712.             int code = (int)wParam;
  713.             CFlexScrollBar *pSB = (CFlexScrollBar *)lParam;
  714.             if (!pSB)
  715.                 return 0;
  716.  
  717.             int nLine = 5;
  718.             int nPage = MulDiv(pSB->GetPage(), 9, 10);
  719.  
  720.             switch (code)
  721.             {
  722.                 case SB_LINEUP: pSB->AdjustPos(-nLine); break;
  723.                 case SB_LINEDOWN: pSB->AdjustPos(nLine); break;
  724.                 case SB_PAGEUP: pSB->AdjustPos(-nPage); break;
  725.                 case SB_PAGEDOWN: pSB->AdjustPos(nPage); break;
  726.                 case SB_THUMBTRACK: pSB->SetPos(pSB->GetThumbPos()); break;
  727.             }
  728.  
  729.             m_nScrollOffset = pSB->GetPos();
  730.  
  731.             Invalidate();
  732.             return 0;
  733.         }
  734.  
  735.         case WM_FLEXHSCROLL:
  736.             assert(0);
  737.         default:
  738.             return CFlexWnd::WndProc(hWnd, msg, wParam, lParam);
  739.     }
  740. }
  741.  
  742. void CDeviceView::ScrollToMakeControlVisible(const RECT &rc)
  743. {
  744.     RECT viewrc;
  745.  
  746.     if (!m_bScrollEnable)
  747.         return;
  748.  
  749.     GetClientRect(&viewrc);
  750.     viewrc.bottom -= g_iListHeaderHeight;
  751.     viewrc.top += m_nScrollOffset;
  752.     viewrc.bottom += m_nScrollOffset;
  753.  
  754.     // If scroll enabled, we scroll the view to make the control visible if not already so.
  755.     if (m_bScrollEnable && m_sb.m_hWnd &&
  756.         !(viewrc.left <= rc.left &&
  757.           viewrc.right >= rc.right &&
  758.           viewrc.top <= rc.top &&
  759.           viewrc.bottom >= rc.bottom))
  760.     {
  761.         // If the callout is below the view window, scroll so it shows up at the bottom of the window.
  762.         if (viewrc.bottom < rc.bottom)
  763.             m_sb.SetPos(m_sb.GetPos() + rc.bottom - viewrc.bottom);
  764.         else
  765.             m_sb.SetPos(rc.top);
  766.         m_nScrollOffset = m_sb.GetPos();
  767.         Invalidate();
  768.     }
  769. }
  770.  
  771. void CDeviceView::SwapControls(int i, int j)
  772. {
  773.     RECT rect;
  774.     CDeviceControl *pTmpControl;
  775.     CDeviceViewText *pTmpViewText;
  776.  
  777.     pTmpControl = m_arpControl[i];
  778.     m_arpControl[i] = m_arpControl[j];
  779.     m_arpControl[j] = pTmpControl;
  780.     pTmpViewText = m_arpText[i];
  781.     m_arpText[i] = m_arpText[j];
  782.     m_arpText[j] = pTmpViewText;
  783.     // Swap the rect back so everything will display properly.
  784.     rect = m_arpControl[i]->GetCalloutMaxRect();
  785.     m_arpControl[i]->SetCalloutMaxRect(m_arpControl[j]->GetCalloutMaxRect());
  786.     m_arpControl[j]->SetCalloutMaxRect(rect);
  787.     rect = m_arpText[i]->GetRect();
  788.     m_arpText[i]->SetRect(m_arpText[j]->GetRect());
  789.     m_arpText[j]->SetRect(rect);
  790.     // Exchange the text rect width, so the correct width stays with the correct text.
  791.     RECT rc1 = m_arpText[i]->GetRect();
  792.     RECT rc2 = m_arpText[j]->GetRect();
  793.     // Store rc1's new width first
  794.     int iTempWidth = rc1.right - (rc2.right - rc2.left);
  795.     rc2.left = rc2.right - (rc1.right - rc1.left);  // Adjust rc2's width
  796.     rc1.left = iTempWidth;  // Adjust rc1's width
  797.     m_arpText[i]->SetRect(rc1);
  798.     m_arpText[j]->SetRect(rc2);
  799. }
  800.  
  801. // Implements a simple selection sort algorithm to sort the control array and viewtext array.
  802. // - iStart is the starting index, inclusive.
  803. // - iEnd is the last index, exclusive.
  804. void CDeviceView::SortCallouts(int iStart, int iEnd)
  805. {
  806.     for (int i = iStart; i < iEnd - 1; ++i)
  807.     {
  808.         DWORD dwSmallestOfs = m_arpControl[i]->GetOffset();
  809.         int iSmallestIndex = i;
  810.         for (int j = i + 1; j < iEnd; ++j)
  811.             if (m_arpControl[j]->GetOffset() < dwSmallestOfs)
  812.             {
  813.                 dwSmallestOfs = m_arpControl[j]->GetOffset();
  814.                 iSmallestIndex = j;
  815.             }
  816.         // Swap the smallest element with i-th element.
  817.         if (iSmallestIndex != i)
  818.             SwapControls(i, iSmallestIndex);
  819.     }
  820. }
  821.  
  822. void CDeviceView::SortAssigned(BOOL bSort)
  823. {
  824.     // If less than 2 controls, no need for sorting.
  825.     if (m_arpControl.GetSize() < 2)
  826.         return;
  827.  
  828.     int iCalloutX[2] = {m_arpControl[0]->GetMinX(), m_arpControl[1]->GetMinX()};  // Callout X for the two columns
  829.  
  830.     // Sort the text array and control array.
  831.     if (bSort)
  832.     {
  833.         // First move all the assigned controls to the first n elements.
  834.         int iNextAssignedWriteIndex = 0;
  835.         for (int i = 0; i < m_arpControl.GetSize(); ++i)
  836.             if (m_arpControl[i]->HasAction())
  837.             {
  838.                 // Swap the controls
  839.                 SwapControls(i, iNextAssignedWriteIndex);
  840.                 ++iNextAssignedWriteIndex;  // Increment the write index
  841.             }
  842.  
  843.         // Sort the two parts now
  844.         SortCallouts(0, iNextAssignedWriteIndex);
  845.         SortCallouts(iNextAssignedWriteIndex, m_arpControl.GetSize());
  846.     } else
  847.         SortCallouts(0, m_arpControl.GetSize());
  848. }
  849.  
  850. void CDeviceView::DoOnPaint(HDC hDC)
  851. {
  852.     // Paint only if we have an update region.
  853.     if (GetUpdateRect(m_hWnd, NULL, FALSE) || m_bForcePaint)
  854.         OnPaint(hDC);
  855.     m_bForcePaint = FALSE;
  856. }
  857.